<style lang="less" scoped>
@import "../assets/css/config.less";
@edit-header:  #F7F6F4;

* {
  margin: 0;
  padding: 0;
}
.flex-row {
  display: flex;
  justify-self: flex-start;
}
.masker{
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  position: fixed;
  z-index: 90;
  opacity: 0;
}

.line1{
  top: 0;
  height: 100%;
  width: 10px;
  margin: 0 -5px;
  transform: translateX(5px);
  z-index: 199;
  cursor: col-resize;
  &:hover+.code-console{
    // border: none;
    border-left: 1px dashed #00ADF0;
  }
}
.line2{
  top: 0;
  height: 100%;
  width: 10px;
  margin: 0 -5px;
  transform: translateX(5px);
  z-index: 199;
  cursor: col-resize;
  &:hover+.code-iframe{
    // border: none;
    border-left: 1px dashed #00ADF0;
  }
}
.code-editor{
  min-width: 100px;
  background-color: #F7F7F7;
}
.code-iframe{
  // flex: 1;
  min-width: 100px;
  border: @border;
  iframe{
    outline: none;
    border: none;
    width: 100%;
    height: 95%;
  }
}
.code-main-area{
  height: 100%;
}
.code-console{
  border: @border;
  border-right: none;
  height: 100%;
  min-width: 100px;
  z-index: 95;
  background-color: rgba(255, 255, 255, 1);
  display: flex;
  flex-direction: column;
  .console-header{
    display: flex;
    justify-content: space-between;
    align-items: center;
    .clear{
      border: 1px solid @color2;
      outline: none;
      margin: 5px 10px ;
      padding: 0px 10px;
      line-height: 18px;
      border-radius: 9px;
      font-size: 14px;
      background-color: @color6;
      color: @color2;
      cursor: pointer;
      &:hover{
        background-color: @color2;
        color: #fff;
      }
    }
  }
  .console-area {
    position: relative;
    width: 100%;
    height: 100%;
    padding: 0 0 0px 0;
    font-size: 14px;
    overflow: hidden;
    word-break: break-all;
    word-wrap: break-word;
  }
}
.flex-end {
  display: flex;
  justify-content: flex-end;
}

.title {
  margin: 10px 0;
  font-size: 10px;
}
.demo-name {
  margin: 10px;
}
#runnob {
  padding-bottom: 40px;
}

.tabs-container {
  width: 90%;
}

.tabs-body {
  height: 400px;
}

.display {
  font-size: 14px;
  padding: 5px;
  font-weight: bold;
}

.card {
  width: 100%;
  margin-top: 10px;
  margin-bottom: 20px;
  position: relative;
  // z-index: 800;
  border: 1px solid rgb(236, 235, 235);
  padding: 20px;
  border-radius: 0 4px 4px 4px;
  box-shadow: 0 0 6px 0 #ccc;
  div,
  p {
    color: #3b3d45;
    font-size: 16px;
    text-align: left;
  }
}

.disabled {
  opacity: 0.6;
}
#runnob{
  width: 100%;
  height: 100%;
  padding-top: 58px;
}
.header {
  position: relative;
  margin-top: -58px;
  height: 58px;
  min-width: 1210px;
  width: 100%;
  z-index: 100;
}
.content {
  display: flex;
  flex-direction: column;
  min-width: 1210px;
  margin-top: 10px;
  height: 100%;
}
.flex {
  display: flex;
  justify-content: space-between;
  margin-bottom: 8px;
}
.right {
  position: absolute;
  right: 0;
  margin-right: 100px;
}

.change_color:hover {
  color: @color1;
}

.column {
  display: flex;
  flex-direction: column;
}
.tab-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: @edit-header;
  border-bottom: 2px solid #bdb6a5;
  overflow: visible;
  position: relative;
  .tab-item{
    font-size: 13px;
    width: 70px;
    line-height: 36px;
    text-align: center;
    color: #000;
    border-right: 2px solid #bdb6a5;
    cursor: pointer;
    overflow: visible;
    &.active{
      background-color: #F7F7F7;
      color: @color1;
      border-bottom: none;
      cursor: default;
      overflow: visible;
      &::after{
        content: '';
        position: absolute;
        top: 0;
        display: block;
        width: 68px;
        height: 4px;
        background-color: #F7F7F7;
        transform: translateY(35px);
      }
    }
  }
}

.bottom-mask {
  position: relative;
  z-index: 99;
  background-color: @white;
  height: 8px;
  width: 100%;
  transform: translateY(-4px);
}
.button-submit {
  margin: 0 10px 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #E5E6E1;
  border: 2px outset #7ebdf0;
  border-radius: 50%;
  color: @color1;
  font-size: 14px;
  width: 24px;
  height: 24px;
  outline: none;
  cursor: pointer;
  .icon{
    font-weight: 600;
    transform: translateX(1px);
  }
}
.button-submit:hover {
  border: 2px outset #52A1FF;
  color:@color1;
  // transform: translateY(1px);
}
.button-submit:active {
  border: 1px outset #b6b3e9;
  color: @color1;
}
#runnob .button {
  line-height: 36px;
  background-color: @color6;
  border: 1px solid @color1;
  color: @color2;
  border-radius: 18px;
  font-size: 14px;
  width: 120px !important;
  margin: 10px 4px;
  letter-spacing: 2px;
  outline: none;
  cursor: pointer;
  &:hover {
    background-color: @color1;
    color: @white;
  }
}
.button2 {
  line-height: 36px;
  background-color: @color6;
  border: 1px solid @color1;
  color: @color2;
  border-radius: 4px;
  font-size: 14px;
  width: 120px;
  margin: 10px 4px;
  letter-spacing: 2px;
  outline: none;
  cursor: pointer;
  &:hover {
    background-color: @color1;
    color: @white;
  }
  &:disabled {
    background-color: rgb(253, 249, 247);
    border-color: @color5;
    color: @color4;
    cursor: not-allowed;
  }
}
.button3 {
  line-height: 32px;
  background-color: @color1;
  border: 1px solid @color1;
  color: @white;
  border-radius: 4px;
  font-size: 14px;
  width: 80px;
  letter-spacing: 2px;
  outline: none;
  cursor: pointer;
  &:hover {
    background-color: @color2;
    color: @white;
  }
  &:active {
    background-color: @color3;
    color: @white;
  }
}
input {
  line-height: 18px;
  height: 35px;
  width: 320px;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  padding: 5px 15px;
  font-size: 14px;
}
input:hover {
  border: @border-hover;
}
input:focus {
  border-color: @color1;
  box-shadow: none;
  outline: none;
}
</style>
<style lang="less">
@import "../assets/css/config.less";
@import "../assets/css/CodeMirror.css";
// 覆盖组件样式
.CodeMirror-scroll{
  height: 500px;
}
.CodeMirror {
  height: 100% !important;
}

#runnob .el-dialog__headerbtn .el-dialog__close,
#runnob .el-message-box__headerbtn .el-message-box__close {
  color: #909399 !important;
  &:hover {
    color: @color1 !important;
  }
}
#runnob .el-button--primary {
  background-color: @color1 !important;
  border: none !important;
  &:hover {
    background-color: @color2 !important;
  }
}
#runnob .el-checkbox__inner {
  &:hover {
    border-color: @color1;
  }
}
#runnob .el-checkbox__input.is-checked .el-checkbox__inner,
#runnob .el-checkbox__input.is-indeterminate .el-checkbox__inner {
  background-color: @color1;
  border-color: @color1;
}
#runnob .el-checkbox__input.is-checked + .el-checkbox__label {
  color: @color1;
}
#runnob .el-select-dropdown__item.selected {
  color: @color1 !important;
}
#runnob .el-input--small .el-input__inner {
  border-color: #c0c4cc;
}
#runnob .el-select .el-input__inner:focus {
  border-color: @color1 !important;
  box-shadow: none !important;
}
#runnob .el-select .el-input.is-focus .el-input__inner {
  border-color: @color1;
  box-shadow: none;
}

</style>
<template>
  <div id="runnob" style="text-align: left">
    <div class="header">
      <myheader></myheader>
    </div>
    <div id="content" class="content">
      <div v-if="fromFlag !== 'demoPractise-add' && fromFlag !== 'editPoint'">
        <el-row
          class="title"
          type="flex"
          justify="center"
        >
          <el-col :span="22">
            <div style="font-size: 20px; font-weight: bold">
              {{ hint }}
            </div>
          </el-col>
        </el-row>
        
      </div>

      <div v-if="fromFlag === 'demoPractise-add'">
        <el-row
          class="title"
          type="flex"
          justify="center"
        >
          <el-col :span="22">
            <h1 class="demo-name">案例名称:</h1>
            <el-input
              v-model="demoName"
              placeholder="案例名称"
              style="margin-bottom: 10px"
            ></el-input>
          </el-col>
        </el-row>
      </div>

      <div v-if="fromFlag === 'editPoint'">
        <el-row
          class="title"
          type="flex"
          justify="center"
        >
          <el-col :span="22">
            <h1 class="demo-name">练习描述:</h1>
            <el-input
              v-model="codeDemoTip"
              placeholder="练习描述"
              style="margin-bottom: 10px"
            ></el-input>
          </el-col>
        </el-row>
      </div>
      <!-- <el-row style="padding-bottom: 30px; " type="flex" justify="center">
      </el-row> -->
      <!-- <el-row style="padding-bottom: 30px; " type="flex" justify="center">
        <el-col :span="22">
          <div>{{record}}</div>
        </el-col>
      </el-row> -->
      <el-row style="flex: 1" type="flex" justify="center">
        <el-col :span="22">
          <div id="code-area" class="flex-row code-main-area">
            <div id="editor" class="code-editor" :style="`width:${codeEdtiorWidth}px`">
              <div class="tab-header">
                <div class="flex-row">
                  <div
                    class="tab-item"
                    :class="isactive === index ? 'active' : ''"
                    v-for="(item, index) in code_type"
                    :key="index"
                    @click="codeTypeClick(index)"
                  >
                    {{ item }}
                  </div>
                </div>
                <div class="flex-row">
                  <button 
                    class="button-submit" 
                    v-if="pageFlag === 'codeDemoPractise' || pageFlag === 'demoPractise'" 
                    title="收藏当前代码" 
                    @click="collect"
                  >
                    <icon class="el-icon-star-off icon" size="14" />
                    <!-- collect -->
                  </button>
                  <button 
                    class="button-submit" 
                    title="重置为初始代码" 
                    v-if="pageFlag === 'codeDemoPractise' || pageFlag === 'demoPractise'" 
                    @click="reset"
                  >
                    <icon class="el-icon-refresh-left icon" size="14" />
                    <!-- reset -->
                  </button>
                  <button 
                    class="button-submit" 
                    title="保存为案例代码" 
                    v-if="pageFlag === 'addDemo'" 
                    @click="addDemo"
                  >
                    <icon class="ivu-icon-ios-cloud-upload-outline icon" size="15" />
                    <!-- save -->
                  </button>
                  <button 
                    class="button-submit" 
                    title="保存为教程示例代码" 
                    v-if="pageFlag==='addCodeDemo'" 
                    @click="addCodeDemo"
                  >
                    <icon class="ivu-icon-ios-cloud-upload-outline icon" size="15" />
                    <!-- save -->
                  </button>
                  <button 
                    class="button-submit" 
                    title="更新代码" 
                    v-if="pageFlag==='updateCodeDemo'" 
                    @click="UpdateCodeDemo"
                  >
                    <icon class="el-icon-refresh icon" size="14" />
                    <!-- update -->
                  </button>
                  <button 
                    class="button-submit" 
                    title="运行程序" 
                    @click="concat"
                  >
                    <icon class="ivu-icon-ios-play-outline icon" size="16" />
                    <!-- <Icon type="ios-play-outline" color="#9F0EB5" size="14" /> -->
                    <!-- run -->
                  </button>
                </div>
              </div>
              <div>
                <div v-if="isactive === 1">
                  <codemirror
                    class="code-mirror"
                    v-model="html_code"
                    :options="html_option"
                    @change="html_change"
                    @focus.native.capture="
                      currentEdit !== 'html' ? sendRecord : ''
                    "
                  ></codemirror>
                </div>
                <div v-if="isactive === 0">
                  <codemirror
                    class="code-mirror"
                    v-model="js_code"
                    :options="js_option"
                    ref="js_editor"
                    @keyup.native="showCodeTips($event, 'js_editor')"
                    @change="js_change"
                    @focus.native.capture="
                      currentEdit !== 'js' ? sendRecord : ''
                    "
                  ></codemirror>
                </div>
                <div v-if="isactive === 2">
                  <codemirror
                    class="code-mirror"
                    v-model="css_code"
                    :options="css_option"
                    ref="css_editor"
                    @keyup.native="showCodeTips($event, 'css_editor')"
                    @change="css_change"
                    @focus.native.capture="currentEdit !== 'css' ? sendRecord : ''"
                  ></codemirror>
                </div>
              </div>
            </div>
            <div id="line1" class="line1"></div>
            <div id="console" class="code-console" :style="`width:${codeConsoleWidth}px`">
              <div class="console-header">
                <div class="display">控制台(console)</div>
                <button class="clear" @click="clearConsole">clear</button>
              </div>
              <div class="console-area" ref="console"></div>
            </div>
            
            <div id="line2" class="line2"></div>
            <div id="iframe" class="code-iframe" :style="`width:${codeShowWidth}px`">
              <div class="display">预览效果</div>
              <iframe
                ref="iframeBox"
                v-bind:srcdoc="present"
              ></iframe>
            </div>
          </div>
        </el-col>
      </el-row>
    </div>
    <div v-if="resizing" class="masker"></div>
  </div>
</template>
<script>
import header from "./header.vue";
import { codemirror } from "vue-codemirror-lite";
import resizeFun from "../utils/resize"

import CodeMirror from "codemirror";
const codemirrorEmmet = require('codemirror-emmet')

require("codemirror/lib/codemirror.css");
require("codemirror/mode/javascript/javascript");
require("codemirror/mode/css/css");
require("codemirror/mode/xml/xml");
require("codemirror/mode/vue/vue");
require("codemirror/addon/hint/show-hint.js");
require("codemirror/addon/hint/show-hint.css");
require("codemirror/addon/hint/javascript-hint.js");
require("codemirror/addon/hint/css-hint.js");
require("codemirror/addon/hint/xml-hint.js");

require("codemirror/addon/lint/lint.js");
require("codemirror/addon/lint/javascript-lint.js");
require("codemirror/addon/lint/css-lint.js");
require("codemirror/addon/lint/lint.css");
require("codemirror/addon/lint/html-lint.js");
require("htmlhint");
import { JSHINT } from "jshint";
import { CSSLint } from "csslint";
window.JSHINT = JSHINT;
window.CSSLint = CSSLint;
require("codemirror/addon/selection/active-line.js");
require("codemirror/addon/selection/active-line.js");
require("codemirror/addon/edit/closetag.js");
require("codemirror/addon/edit/closebrackets.js");
require("codemirror/addon/edit/matchbrackets.js");

require("codemirror/addon/comment/comment.js");
require("codemirror/addon/wrap/hardwrap.js");
require("codemirror/addon/fold/foldcode.js");
require("codemirror/addon/fold/foldgutter.js");
require("codemirror/addon/fold/foldgutter.css");
require("codemirror/addon/fold/comment-fold.js");
require("codemirror/addon/fold/brace-fold.js");
require("codemirror/keymap/sublime.js");
require("codemirror/theme/3024-day.css");

require("codemirror/addon/scroll/annotatescrollbar.js");
require("codemirror/addon/search/matchesonscrollbar.js");
require("codemirror/addon/search/match-highlighter.js");
require("codemirror/addon/search/jump-to-line.js");

require("codemirror/addon/dialog/dialog.js");
require("codemirror/addon/dialog/dialog.css");
require("codemirror/addon/search/searchcursor.js");
require("codemirror/addon/search/search.js");

// require("codemirror/addon/scroll/simplescrollbars.css");
require("codemirror/addon/scroll/simplescrollbars.js");

import { mapMutations, mapState } from "vuex";
import {
  ONLINE_CODING_SUBMIT,
  ONLINE_CODING_REALTIME,
  ONLINE_CODING_LEAVETIME,
} from "../constants";
import {
  saveCodeRecord,
  getCode,
  addDemoCode,
  getLastCode,
  add_codeCollection,
  add_demo,
  get_demo,
  update_demo,
  add_logs
} from "../api/api";
import Recorder from "js-audio-recorder";
import InDConsole from "../utils/inDConsole";
let recorder = new Recorder();
export default {
  name: "runnob",
  data() {
    return {
      present: "",
      console_this: "",
      html_code: "",
      css_code: "",
      js_code: "",
      html: "",
      css: "",
      js: "",
      hint: "题目描述",
      codeDemoTip: "",
      records: this.record,
      unloadflag: true,
      userUUID: "",
      currentEdit: "",
      point: {},
      startTime: Date,
      isChange: false, //标识是否修改了代码
      // urlCoding: "http://localhost:8081/api/saveCodeRecord",
      urlCoding: "https://www.ourspark.org/api/saveCodeRecord",
      demoId: "", // 案例练习
      codeDemoId: "", // 教程穿插代码练习 id
      fromFlag: "",
      demoName: "",
      code_type: ["js代码", "html代码", "css代码"],
      isactive: 0,
      iframeWin: null,
      pageFlag: "", // 当前是哪个页面 codeDemoPractise、addCodeDemo、updateCodeDemo(前三个是来自教程界面）、demoPractise、addDemo、updateDemo（后三个来自案例界面）
      codeEdtiorWidth: 0,
      codeConsoleWidth: 0,
      codeShowWidth: 0,
      resizing: false,
      resizeFunc: ()=>{
        this.codeEdtiorWidth = this.codeConsoleWidth = this.codeShowWidth = document.getElementById("code-area").clientWidth/3;
      }
    };
  },
  created(){
    codemirrorEmmet(CodeMirror);
  },
  mounted() {
    window.addEventListener("resize", this.resizeFunc)
    this.startTime = +new Date();
    this.userUUID = this.$store.state.UUID;
    if (this.$route.query.id) {
      // 来自教程中的代码练习部分
      this.point._id = "";
      this.codeDemoId = this.$route.query.id;
      this.pageFlag = 'codeDemoPractise';
      get_demo({
        id: this.$route.query.id,
      }).then((res) => {
        if (res.state !== true) {
          this.$Message.error(res.msg);
          return;
        }
        this.point._id = res.msg.point_id
        this.hint = res.msg.description;
        this.html_code = res.msg.html_content;
        this.css_code = res.msg.css_content;
        this.js_code = res.msg.js_content;
        this.present = `<html>${res.msg.html_content}<\/html><style>${res.msg.css_content}<\/style><script>${res.msg.js_content}<\/script>`;

        this.html = "<html>" + this.html_code + "</html>";
        this.css = "<style>" + this.css_code + "</style>";
        this.js =
          '<script type="text/javascript">' + this.js_code + `<\/script>`;
        getLastCode({
          UUID: this.userUUID,
          codeDemoId: this.codeDemoId,
        }).then((res) => {
          if (res.state !== true) {
            return this.$Message.error(res.msg);
          }
          if (res.msg === "no" || res.code.length===0) {
            return;
          }
          this.html_code = res.code.html;
          this.css_code = res.code.css;
          this.js_code = res.code.js;
          this.present = `<html>${res.code.html}<\/html><style>${res.code.css}<\/style><script>${res.code.js}<\/script>`;
          this.html = "<html>" + this.html_code + "</html>";
          this.css = "<style>" + this.css_code + "</style>";
          this.js =
            '<script type="text/javascript">' + this.js_code + `<\/script>`;
        });
      });
    } else {
      let codemes = JSON.parse(sessionStorage.getItem("runnob"));
      this.fromFlag = codemes.from;
      switch (codemes.from) {
        case "demoPractise-add":
          // 来自案例实践  添加案例
          this.point._id = "";
          this.pageFlag = 'addDemo';
          break;
        case "editPoint":
          this.point._id = codemes.pointId;
          if (!codemes.demoId) {
            // 添加新的练习代码
            this.pageFlag = "addCodeDemo";
            break;
          }
          // 修改练习代码
          this.pageFlag = "updateCodeDemo";
          this.codeDemoId = codemes.demoId;
          get_demo({
            id: this.codeDemoId,
          }).then((res) => {
            if (res.state !== true) {
              this.$Message.error(res.msg);
              return;
            }
            // this.codeDemoTip = res.msg.description;
            this.html_code = res.msg.html_content;
            this.css_code = res.msg.css_content;
            this.js_code = res.msg.js_content;
            this.present = `<html>${res.msg.html_content}<\/html><style>${res.msg.css_content}<\/style><script>${res.msg.js_content}<\/script>`;

            this.html = "<html>" + this.html_code + "</html>";
            this.css = "<style>" + this.css_code + "</style>";
            this.js =
              '<script type="text/javascript">' + this.js_code + `<\/script>`;
          });
          break;
        case "demoPractise":
          // 来自案例实践  进行实践
          this.point._id = "";
          this.demoId = codemes.demoId;
          this.pageFlag = 'demoPractise';
          getCode({
            id: codemes.demoId,
          }).then((res) => {
            if (res.state === true) {
              this.hint = "案例：" + res.code.name;
              this.html_code = res.code.html_content;
              this.css_code = res.code.css_content;
              this.js_code = res.code.js_content;
              this.present = `<html>${res.code.html_content}<\/html><style>${res.code.css_content}<\/style><script>${res.code.js_content}<\/script>`;

              this.html = "<html>" + this.html_code + "</html>";
              this.css = "<style>" + this.css_code + "</style>";
              this.js =
                '<script type="text/javascript">' + this.js_code + `<\/script>`;
            }
          });
          getLastCode({
            UUID: this.userUUID,
            demoId: this.demoId,
          }).then((res) => {
            if (res.state === true) {
              // this.hint = "案例：" + res.name;
              this.html_code = res.code.html;
              this.css_code = res.code.css;
              this.js_code = res.code.js;
              this.present = `<html>${res.code.html}<\/html><style>${res.code.css}<\/style><script>${res.code.js}<\/script>`;
              this.html = "<html>" + this.html_code + "</html>";
              this.css = "<style>" + this.css_code + "</style>";
              this.js =
                '<script type="text/javascript">' + this.js_code + `<\/script>`;
            }
          });
          break;
       
      }
    }

    InDConsole.init(this.$refs.console);
    this.iframeWin = this.$refs.iframeBox.contentWindow;
    window.addEventListener("message", this.renderConsole);
    
    this.codeEdtiorWidth = this.codeConsoleWidth = this.codeShowWidth = document.getElementById("code-area").clientWidth/3
    const ele = document.querySelector("#line1");
    ele.addEventListener("mousedown", this.resizeConsole("#line1", "codeEdtiorWidth", 350, "codeConsoleWidth",260));
    const ele2 = document.querySelector("#line2");
    ele2.addEventListener("mousedown", this.resizeConsole("#line2", "codeConsoleWidth", 260, "codeShowWidth", 260));
  },
  beforeRouteLeave(to, from, next) {
    if (this.pageFlag === "addCodeDemo"||this.pageFlag === "addDemo") {
      next(true);
    }
    // 移除console相关事件监听
    window.removeEventListener("resize", this.resizeFunc);
    window.removeEventListener("message", this.renderConsole);
    const ele = document.querySelector("#line1");
    ele.removeEventListener("mousedown", this.resizeConsole("#line1", "codeEdtiorWidth", 350, "codeConsoleWidth",260));
    const ele2 = document.querySelector("#line2");
    ele2.removeEventListener("mousedown", this.resizeConsole("#line2", "codeConsoleWidth", 260, "codeShowWidth", 260));
  
    if (this.isChange) {
      this.beforeLeaveSendRecord();
      next(true);
    } else {
      next(true);
    }
  },

  computed: {
    html_option: function () {
      return {
        mode: "text/html",
        tabSize: 2,
        lineNumbers: true,
        lineWrapping: true,
        styleActiveLine: true,
        autoCloseTags: true,
        showCursorWhenSelecting: true,
        theme: "3024-day",
        foldGutter: true,
        lineWrapping: true,
        matchBrackets: true,
        scrollbarStyle: "simple",
        gutters: [
          "CodeMirror-linenumbers",
          "CodeMirror-foldgutter",
          "CodeMirror-lint-markers",
        ],
        keyMap: "sublime",
        lint: true,
        extraKeys: {
          "Ctrl-Alt": "autocomplete",
          "Ctrl-Q": (cm) => {
            cm.foldCode(cm.getCursor());
          },
          Tab: (cm) => {
            if (cm.somethingSelected()) {
              // 当选中文本并按下tab，文本整体缩进
              cm.indentSelection("add");
            } else if (cm.getOption("mode").indexOf("html") > -1) {
              // 当前的mode为html时，执行命令emmetExpandAbbreviation
              try {
                if (cm.execCommand("emmetExpandAbbreviation")!==true) {
                  const spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
                  cm.replaceSelection(spaces, "end", "+input");
                }
              } catch (err) {
                console.log(err);
              }
            } else {
              // 前面两个条件都不满足，按下tab就正常缩进
              const spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
              cm.replaceSelection(spaces, "end", "+input");
            }
          },
        }
      };
    },
    js_option: function () {
      return {
        mode: "text/javascript",
        tabSize: 2,
        lineNumbers: true,
        lineWrapping: true,
        styleActiveLine: true,
        showCursorWhenSelecting: true,
        theme: "3024-day",
        foldGutter: true,
        lineWrapping: true,
        gutters: [
          "CodeMirror-linenumbers",
          "CodeMirror-foldgutter",
          "CodeMirror-lint-markers",
        ],
        // keyMap: "sublime",
        matchBrackets: true,
        lint: true,
        autoCloseBrackets: true,
        scrollbarStyle: "simple",
        extraKeys: {
          "Ctrl-Alt": "autocomplete",
          "Ctrl-Q": (cm) => {
            cm.foldCode(cm.getCursor());
          },
          "Ctrl-/": (cm) => {
            cm.toggleComment({ indent: true });
          },
          Tab: (cm) => {
            if (cm.somethingSelected()) {
              // 当选中文本并按下tab，文本整体缩进
              cm.indentSelection("add");
            } else if (cm.getOption("mode").indexOf("javascript") > -1) {
              try {
                if (cm.getTokenAt(cm.getCursor()).string.match(/[a-zA-Z]+/g)) {
                  cm.execCommand("autocomplete");
                } else {
                  const spaces = Array(cm.getOption("indentUnit") + 1).join(
                    " "
                  );
                  cm.replaceSelection(spaces, "end", "+input");
                }
              } catch (err) {
                console.error(err);
              }
            } else {
              // 前面两个条件都不满足，按下tab就正常缩进
              const spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
              cm.replaceSelection(spaces, "end", "+input");
            }
          },
        },
        hintOptions: {
          completeSingle: false,
        },
      };
    },
    css_option: function () {
      return {
        mode: "text/css",
        tabSize: 2,
        lineNumbers: true,
        lineWrapping: true,
        styleActiveLine: true,
        theme: "3024-day",
        foldGutter: true,
        lineWrapping: true,
        showCursorWhenSelecting: true,
        gutters: [
          "CodeMirror-linenumbers",
          "CodeMirror-foldgutter",
          "CodeMirror-lint-markers",
        ],
        autoCloseBrackets: true, // 自动输入括弧
        keyMap: "sublime",
        matchBrackets: true,
        lint: true,
        scrollbarStyle: "simple",
        extraKeys: {
          "Ctrl-Alt": "autocomplete",
          "Ctrl-Q": (cm) => {
            cm.foldCode(cm.getCursor());
          },
          Tab: (cm) => {
            if (cm.somethingSelected()) {
              // 当选中文本并按下tab，文本整体缩进
              cm.indentSelection("add");
            } else if (cm.getOption("mode").indexOf("css") > -1) {
              try {
                if (cm.getTokenAt(cm.getCursor()).string.match(/[a-zA-Z]+/g)) {
                  cm.execCommand("autocomplete");
                } else {
                  const spaces = Array(cm.getOption("indentUnit") + 1).join(
                    " "
                  );
                  cm.replaceSelection(spaces, "end", "+input");
                }
              } catch (err) {
                console.error(err);
              }
            } else {
              // 前面两个条件都不满足，按下tab就正常缩进
              const spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
              cm.replaceSelection(spaces, "end", "+input");
            }
          },
        },
        hintOptions: {
          completeSingle: false,
        },
      };
    },
    ...mapState(["record"]),
  },
  methods: {
    ...mapMutations([
      "add", // 将 `this.add(payload)` 映射为 `this.$store.commit('add', payload)`
      "clear",
    ]),
    beforeLeaveSendRecord(){
      const that = this;
      that.add({
        type: ONLINE_CODING_LEAVETIME,
        content: {
          html: that.html_code,
          css: that.css_code,
          js: that.js_code,
          user: that.userUUID,
          time: +new Date(),
          leaveTime: +new Date(),
          pointId: that.point._id,
          demoId: that.demoId,
          startTime: that.startTime,
        },
      });
      let sendCount = that.record.length;
      let data = new FormData();
      data.append("codeRecord_list", JSON.stringify(that.record));
      data.append("user", JSON.stringify(that.userUUID));
      data.append("startTime", JSON.stringify(that.startTime));
      data.append("pointId", JSON.stringify(that.point._id));
      data.append("demoId", JSON.stringify(that.demoId));
      data.append("codeDemoId", JSON.stringify(that.codeDemoId));
      data.append("flag", JSON.stringify(true));
      navigator.sendBeacon(that.urlCoding, data);
      that.clear(sendCount);
    },

    clearConsole() {
      InDConsole.clear();
    },
    resizeConsole(element, left, minleft=100, right, minright=100){
      // 这里过滤掉非目标元素
      let that = this;
      return function(e){
        const ele = document.querySelector(element);
        if (e.target !== ele) {
          return;
        }
        const cannotSelect = function() {
          return false;
        }
        document.body.onselectstart = document.body.ondrag = cannotSelect;
        that.resizing = true;
        let x0 = e.clientX;
        const handleMove = ({ clientX, target }) => {
        // 本次位置和上次位置的变化量
          let changeWidth = clientX - x0;
          if(that[left] + changeWidth < minleft){
            that[right] = that[right] - minleft + that[left];
            x0 = x0 + minleft - that[left];
            that[left] = minleft;
          } else if(that[right] - changeWidth < minright){
            that[left] = that[left] + that[right] - minright;
            x0 = x0 - minright + that[right];
            that[right] = minright;
          } else {
            that[left] += changeWidth;
            that[right] -= changeWidth;
            x0 = clientX;
          }
        };
        document.addEventListener("mousemove", handleMove);
        document.addEventListener("mouseup", () => {
          document.body.onselectstart = document.body.ondrag = null;
          document.removeEventListener("mousemove", handleMove);
          that.resizing = false;
        });

      }
    },
    renderConsole(res){
      if (res.data.status === "ok") {
        if(!this.iframeWin.consoleInfo){
          return
        }
        const num = this.iframeWin.consoleInfo.length;
        for(let i=0; i<num; i++){
          if(this.pageFlag==='codeDemoPractise' || this.pageFlag==='demoPractise'){
            var log = {};
            const item = this.iframeWin.consoleInfo[i];
            if(this.iframeWin.consoleInfo[i] && this.iframeWin.consoleInfo[i].type==='error2') {
              log.type = item.type;
              log.content = [];
              log.content.push({
                msg: item.data.msg.error.stack,
                lineno: item.data.msg.lineno-item.data.linenum-82
              })
            } else {
              log.type = item.type;
              log.content = item.data
            }
            console.log(log)
            add_logs({
              UUID: this.userUUID,
              html: this.html_code,
              js: this.js_code,
              css: this.css_code,
              type: log.type,         // 日志类型 
              codeDemoId: this.codeDemoId,   // 代码练习 id ，教程示例中的
              pointId: this.point._id,      // 示例所属知识点 id
              demoId: this.demoId,       // 案例 id，案例中的
              title: this.hint,        // 案例标题或描述
              time: +new Date(),
              logs: log.content,
              startTime: this.startTime,
            }).then(res => {
              if(res.state !== true){
                console.log(res)
              }
            }).catch((err) => {
              console.log(err)
            })
          }
          
          InDConsole.render(this.iframeWin.consoleInfo[i]);
        }
        this.iframeWin.consoleInfo = this.iframeWin.consoleInfo.slice(num);
      }
    },
    concat: function () {
      this.iframeWin.location.reload();
      var that = this;
      // this.present = this.html + this.css + this.js;

      this.present = `

      <!DOCTYPE html>
      <html>
        <head>
          <meta charset="utf-8">
          <script type='text\/javascript'>
            window.consoleInfo = [];
            window.addEventListener('error',function(e){
                consoleInfo.push({
                  data: {
                    msg: e, 
                    linenum: ${that.css_code.split(/\r\n|\r|\n/).length+that.html_code.split(/\r\n|\r|\n/).length}}
                  ,
                  type: 'error2'
                })
                window.parent.postMessage({
                  'status': 'ok'
                }, '*');
                e.preventDefault();
            });
            RegExp.prototype.clone = function() {
              var pattern = this.valueOf();
              var flags = '';
              flags += pattern.global ? 'g' : '';
              flags += pattern.ignoreCase ? 'i' : '';
              flags += pattern.multiline ? 'm' : '';
              return new RegExp(pattern.source, flags);
            };

          <\/script>
          <style>${that.css_code}<\/style>
        <\/head>
        <body>
          ${that.html_code}
          <script>
            function deepClone(source){
              const targetObj = source.constructor === Array ? [] : {}; \/\/ 判断复制的目标是数组还是对象
              for(let keys in source){ \/\/ 遍历目标
                if(source.hasOwnProperty(keys)){
                  if(source[keys]){
                    switch(Object.prototype.toString.call(source[keys])){
                      case '[object Array]':
                        \/\/ 如果值是对象，就递归一下
                        targetObj[keys] = [];
                        targetObj[keys] = deepClone(source[keys]);
                        break;
                      case '[object Object]':
                        targetObj[keys] = {};
                        targetObj[keys] = deepClone(source[keys]);
                        break;
                      case '[object Date]':
                        targetObj[keys] = new Date(source[keys].valueOf());
                        break;
                      case '[object RegExp]':
                        targetObj[keys] = source[keys].clone();
                        break;
                      default:
                        targetObj[keys] = source[keys];
                    }
                  
                  }else{ \/\/ 如果不是，就直接赋值
                    targetObj[keys] = source[keys];
                  }
                } 
              }
              return targetObj;
            }

            if (console) {
              const ableMethods = ['log', 'info', 'debug', 'warn', 'error']
              for (let item of ableMethods) {
                console[item] = function () {
                  let args = Array.prototype.slice.apply(arguments);
                  consoleInfo.push({
                    data: deepClone(args), 
                    type: item
                  })
                  window.parent.postMessage({
                    'status': 'ok'
                  }, '*');
                }
              }
            }
            ${that.js_code}
          <\/script>
        <\/body>
      <\/html>`;
      if (
        this.fromFlag === "demoPractise-add" ||
        this.fromFlag === "editPoint"
      ) {
        //编辑案例不做记录
        return;
      }
      this.add({
        type: ONLINE_CODING_SUBMIT,
        content: {
          html: that.html_code,
          css: that.css_code,
          js: that.js_code,
          user: that.userUUID,
          time: +new Date(),
          pointId: this.point._id,
          demoId: that.demoId,
          codeDemoId: this.codeDemoId,
          startTime: this.startTime,
        },
      });

      if (this.record.length === 0) {
        return;
      }
      let sendCount = this.record.length;
      saveCodeRecord({
        codeRecord_list: this.record,
        user: that.userUUID,
        startTime: this.startTime,
        pointId: this.point._id,
        demoId: that.demoId,
        codeDemoId: this.codeDemoId,
      })
        .then((response) => {
          if (response.state === true) {
            this.clear(sendCount);
          } else {
            that.$Message.error(response.msg);
          }
        })
        .catch((error) => {
          that.$Message.error(error.status);
        });
    },
    collect(){
      if(!this.isChange) {
        this.$Message.warning('请先修改代码！'); 
        return 
      }
      this.isChange = false;
      this.$prompt('请为本次收藏添加标题', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        inputPattern: /.+/,
        inputErrorMessage: '标题不能为空'
      }).then(({ value }) => {
        add_codeCollection({
          UUID: this.userUUID,
          pointId: this.point._id,
          demoId: this.demoId,
          codeDemoId: this.codeDemoId,     // 代码练习 id ，教程示例中的
          js: this.js_code,
          css: this.css_code,
          html: this.html_code,
          title: this.hint,          // 案例本身标题
          des: value,            // 自定义的本条收藏的标题
          time: +new Date()  
        }).then(res => {
          if(res.state===false){
            this.$Message.error('收藏代码失败！')
            this.isChange = true;
          } else {
            this.$Message.success('收藏代码成功！')
          }
        }).catch(err => {
          this.$Message.error('收藏代码失败！')
          this.isChange = true;
        })
      }).catch(() => {
        this.$Message.warning('取消收藏');   
        this.isChange = true;    
      });
      
    },
    closeit() {},
    codeTypeClick(index) {
      this.isactive = index;
    },

    reset() {
      if (this.$route.query.id) {
        this.point._id = "";
        this.codeDemoId = this.$route.query.id;
        get_demo({
          id: this.$route.query.id,
        }).then((res) => {
          if (res.state !== true) {
            this.$Message.error(res.msg);
            return;
          }
          this.hint = res.msg.description;
          this.html_code = res.msg.html_content;
          this.css_code = res.msg.css_content;
          this.js_code = res.msg.js_content;
          this.present = `<html>${res.msg.html_content}<\/html><style>${res.msg.css_content}<\/style><script>${res.msg.js_content}<\/script>`;

          this.html = "<html>" + this.html_code + "</html>";
          this.css = "<style>" + this.css_code + "</style>";
          this.js =
            '<script type="text/javascript">' + this.js_code + `<\/script>`;
        });
      } else {
        let codemes = JSON.parse(sessionStorage.getItem("runnob"));
        this.fromFlag = codemes.from;
        switch (codemes.from) {
          case "demoPractise-add":
            this.point._id = "";
            break;
          case "editPoint":
            this.point._id = codemes.pointId;
            break;
          case "demoPractise":
            this.point._id = "";
            this.demoId = codemes.demoId;
            getCode({
              id: codemes.demoId,
            }).then((res) => {
              if (res.state === true) {
                this.hint = "案例：" + res.code.name;
                this.html_code = res.code.html_content;
                this.css_code = res.code.css_content;
                this.js_code = res.code.js_content;
                this.present = `<html>${res.code.html_content}<\/html><style>${res.code.css_content}<\/style><script>${res.code.js_content}<\/script>`;

                this.html = "<html>" + this.html_code + "</html>";
                this.css = "<style>" + this.css_code + "</style>";
                this.js =
                  '<script type="text/javascript">' +
                  this.js_code +
                  `<\/script>`;
              }
            });
            break;
        }
      }
    },
    UpdateCodeDemo() {
      if (
        this.js_code === "" &&
        this.html_code === "" &&
        this.css_code === ""
      ) {
        this.$alert("案例代码不能为空！", "提示");
        return;
      }
      if (!this.codeDemoTip) {
        this.$alert("代码描述不能为空！", "提示");
        return;
      }
      update_demo({
        id: this.codeDemoId,
        description: this.codeDemoTip,
        js: this.js_code,
        html: this.html_code,
        css: this.css_code,
      })
        .then((res) => {
          if (res.state !== true) {
            this.$Message.error(res.msg);
            return;
          }
          this.$Message.success(res.msg);
          this.$router.go(-1);
        })
        .catch((err) => {
          this.$Message.error(err);
        });
    },
    addCodeDemo() {
      if (
        this.js_code === "" &&
        this.html_code === "" &&
        this.css_code === ""
      ) {
        this.$alert("案例代码不能为空！", "提示");
        return;
      }
      if (!this.codeDemoTip) {
        this.$alert("代码描述不能为空！", "提示");
        return;
      }
      add_demo({
        UUID: this.userUUID,
        description: this.codeDemoTip,
        js: this.js_code,
        html: this.html_code,
        css: this.css_code,
        pointId: this.point._id,
      })
        .then((res) => {
          if (res.state !== true) {
            this.$Message.error("添加练习代码失败");
            return;
          }
          this.$Message.success("添加练习代码成功");
          this.$router.go(-1);
        })
        .catch((err) => {
          this.$Message.error(err);
        });
    },
    addDemo() {
      // 添加案例代码
      if (
        this.js_code === "" &&
        this.html_code === "" &&
        this.css_code === ""
      ) {
        this.$alert("案例代码不能为空！", "提示");
        return;
      }
      if (this.demoName === "") {
        this.$alert("案例名称不能为空！", "提示");
        return;
      }
      addDemoCode({
        name: this.demoName,
        js: this.js_code,
        html: this.html_code,
        css: this.css_code,
      })
        .then((res) => {
          if (res.state === true) {
            this.demoList = res.codeList;
            this.$Message.success("添加案例成功");
          } else {
            this.$Message.error(res.msg);
          }
        })
        .catch((error) => {
          this.$Message.error(error.status);
        });
    },
    createHandler() {
      let killTask = false;
      window.onbeforeunload = (e) => {
        this.beforeLeaveSendRecord();
        // 移除console相关事件监听
        window.removeEventListener("message", this.renderConsole);
        const ele = document.querySelector("#line1");
        ele.removeEventListener("mousedown", this.resizeConsole("#line1", "codeEdtiorWidth", 350, "codeConsoleWidth",260));
        const ele2 = document.querySelector("#line2");
        ele2.removeEventListener("mousedown", this.resizeConsole("#line2", "codeConsoleWidth", 260, "codeShowWidth", 260));
  
        return;
      };
    },
    showCodeTips(e, editor) {
      if (!e.altKey && !e.ctrlKey && /^[a-zA-Z|-]$/.test(e.key)) {
        const cm = this.$refs[editor].editor;
        const str = cm.getTokenAt(cm.getCursor()).string.match(/[a-zA-Z]+/g);
        if (cm.getTokenAt(cm.getCursor()).string.match(/[a-zA-Z]+/g)) {
          cm.execCommand("autocomplete");
        }
      }
    },
    html_change: function (obj) {
      
      this.isChange = true;
      this.currentEdit = "html";
      if (this.unloadflag) {
        this.createHandler();
        this.unloadflag = false;
      }
      var that = this;
      this.html = "<html>" + obj + "</html>";
      if (this.fromFlag === "demoPractise-add") {
        return;
      }
      this.add({
        type: ONLINE_CODING_REALTIME,
        content: {
          html: obj,
          user: this.userUUID,
          time: +new Date(),
          pointId: this.point._id,
          demoId: this.demoId,
          codeDemoId: this.codeDemoId,
          startTime: this.startTime,
        },
      });
      if (this.record.length > 9) {
        let sendCount = this.record.length;
        saveCodeRecord({
          codeRecord_list: this.record,
          user: that.userUUID,
          startTime: this.startTime,
          pointId: this.point._id,
          demoId: that.demoId,
          codeDemoId: this.codeDemoId,
        })
          .then((response) => {
            if (response.state === true) {
              that.clear(sendCount);
            } else {
              that.$Message.error(response.msg);
            }
          })
          .catch((error) => {
            that.$Message.error(error.status);
          });
      }
    },
    css_change: function (obj) {
     
      this.isChange = true;
      this.currentEdit = "css";
      if (this.unloadflag) {
        this.createHandler();
        this.unloadflag = false;
      }
      var that = this;
      this.css = "<style>" + obj + "</style>";
      if (this.fromFlag === "demoPractise-add") {
        return;
      }
      this.add({
        type: ONLINE_CODING_REALTIME,
        content: {
          css: obj,
          user: that.userUUID,
          time: +new Date(),
          pointId: this.point._id,
          demoId: that.demoId,
          codeDemoId: this.codeDemoId,
          startTime: this.startTime,
        },
      });
      if (this.record.length > 9) {
        let sendCount = this.record.length;
        saveCodeRecord({
          codeRecord_list: this.record,
          user: that.userUUID,
          startTime: this.startTime,
          pointId: this.point._id,
          demoId: that.demoId,
          codeDemoId: this.codeDemoId,
        })
          .then((response) => {
            if (response.state === true) {
              that.clear(sendCount);
            } else {
              that.$Message.error(response.msg);
            }
          })
          .catch((error) => {
            that.$Message.error(error.status);
          });
      }
    },
    js_change: function (obj) {
   
      this.isChange = true;
      this.currentEdit = "js";
      if (this.unloadflag) {
        this.createHandler();
        this.unloadflag = false;
      }
      var that = this;
      this.js = '<script type="text/javascript">' + obj + `<\/script>`;
      if (this.fromFlag === "demoPractise-add") {
        return;
      }
      this.add({
        type: ONLINE_CODING_REALTIME,
        content: {
          js: obj,
          user: that.userUUID,
          time: +new Date(),
          pointId: this.point._id,
          demoId: that.demoId,
          codeDemoId: this.codeDemoId,
          startTime: this.startTime,
        },
      });
      if (this.record.length > 9) {
        let sendCount = this.record.length;
        saveCodeRecord({
          codeRecord_list: this.record,
          user: that.userUUID,
          startTime: this.startTime,
          pointId: this.point._id,
          demoId: that.demoId,
          codeDemoId: this.codeDemoId,
        })
          .then((response) => {
            if (response.state === true) {
              that.clear(sendCount);
            } else {
              that.$Message.error(response.msg);
            }
          })
          .catch((error) => {
            this.$Message.error(error.status);
          });
      }
    },
    sendRecord: function () {
      if (this.fromFlag === "demoPractise-add") {
        return;
      }
      let that = this;
      if (this.record.length !== 0) {
        let sendCount = this.record.length;
        saveCodeRecord({
          codeRecord_list: this.record,
          user: that.userUUID,
          startTime: this.startTime,
          pointId: this.point._id,
          demoId: that.demoId,
          codeDemoId: this.codeDemoId,
        })
          .then((response) => {
            if (response.state === true) {
              that.clear(sendCount);
            } else {
              that.$Message.error(response.msg);
            }
          })
          .catch((error) => {
            that.$Message.error(error.status);
          });
      }
    },
  },
  components: {
    myheader: header,
  },
};
</script>
